<!DOCTYPE HTML>
<html lang="en">
<head>
    <title>snake</title>
    <meta charset="utf-8"/>
    <style>
        body {
            background: #eeeeee;
        }

        canvas {
            background: #ffffff;
        }
    </style>
</head>
<body>
<!-- 我们要操作的canvas -->
<canvas id="gbcanvas" width="300" height="300"></canvas>
<br>
<!-- 显示分数的span #score -->
<span>your score : </span><span id="score">0</span>
<br>
<!-- 游戏操作提示 -->
<span>操作方法：W A D S</span>
<br>
<!-- 游戏开始的按钮，点击调用startGame()函数，该函数后面完善 -->
<button onclick="startGame();">Start Game</button>
<script type="text/javascript">
    var canvas = document.getElementById('gbcanvas'); // 这里通过gbCanvas获取canvas对象
    var c = canvas.getContext('2d'); //这里通过canvas获取处理API的上下文context
    var s = document.getElementById('score'); //把游戏的分数显示在这里
    var mWidth = 300; //当前可视区域的宽，即canvas的宽
    var mHeight = 300;  //当前可视区域的高，即canvas的高
    var unit = 5; //设定每个格子的边长
    var mwid = mWidth / unit; //计算当前横向格子数量
    var mhei = mHeight / unit; //计算当前竖向格子数量
    var point = point = {x : 0 , y : 0}; //记录食物的坐标的变量初始化
    var score = 0; //记录成绩的变量初始化
    //注意本对象，并不改变其在画布上的样子，只是负责改变状态，改变样子的另有方法
    //蛇对象
    var shake = {
        startX : 3, //开始头x坐标
        startY : 0, //开始头y坐标
        currOri : 'right', //初始化方向
        ori : [['left' , 'right'] , ['up' , 'down']], //相逆方向数组
        oriss : ['left' , 'right' , 'up' , 'down'], //所有允许的方向，用来判断方向合法性，在canChangeOri方法
        mes : [{x : 3 , y : 0} , {x : 2 , y : 0} , {x : 1 , y : 0}], //初始化蛇的身体坐标，初始长度3
        //坐标为格子坐标非像素坐标
        //添加一个身体的方法
        add : function(){
            //判断当前尾部方向
            var last = this.mes[this.mes.length - 1]; //获取最后一个身体
            var plast = this.mes[this.mes.length - 2]; //获取倒数第二个身体
            var px = last.x - plast.x;
            var py = last.y - plast.y; //根据计算最后两个身体的坐标差，来计算添加身体应在的方向
            //计算新加元素位置
            var newEle = {x : last.x + px , y : last.y + py}; //创建一个新身体
            this.mes.push(newEle); //将新身体假如身体数组
        },
        //移动方向方法，下面几个方法类似，只是方向不同
        moveup : function(){
            var pre = this.mes[0]; //记录第一个身体，即头部的坐标
            this.mes[0] = {x : pre.x , y : pre.y - 1}; //让头部的坐标像上移动一格
            this.move(pre); //调用移动身体的方法
        },
        movedown : function(){
            var pre = this.mes[0];
            this.mes[0] = {x : pre.x , y : pre.y + 1};
            this.move(pre);
        },
        moveleft : function(){
            var pre = this.mes[0];
            this.mes[0] = {x : pre.x - 1 , y : pre.y};
            this.move(pre);
        },
        moveright : function(){
            var pre = this.mes[0];
            this.mes[0] = {x : pre.x + 1 , y : pre.y};
            this.move(pre);
        },
        //移动身体
        move : function(pre){//参数为之前第一个身体，即头部的位置对象
            var tmp;
            for(var i = 1 ; i < this.mes.length ; i++){ //遍历每一个身体节点
                tmp = this.mes[i];
                this.mes[i] = pre;
                pre = tmp;
            } //并且把每个节点的左边变化成前一个节点的坐标，达到依次向前的目的
        },
        //改变方向
        changeOri : function(ori){
            if(this.oriss.indexOf(ori) == -1){ //判断方向是否在允许方向内
                return;
            }
            if(!this.canChangeOri(ori)){ //判断改变方向是否合法
                return;
            }
            this.currOri = ori; //如果上面两个都通过，改变方向
        },
        //判断改变的方向是否合法
        canChangeOri : function(ori){ //参数为方向字符串 例如：left
            if(ori == this.currOri){ //判断方向是否为当前方向，如果是则无需操作
                return false;
            }
            var oris = null;
            for(var i in this.ori){ //判断是否改变方向为当前方向的逆方向
                if(this.ori[i].indexOf(this.currOri) != -1){
                    oris = this.ori[i];
                    break;
                }
            }
            if(oris.indexOf(ori) != -1){
                return false;
            }
            return true;
        },
        //判断是否碰撞到了自己
        isCrashSelf : function(){
            var head = this.mes[0]; //获取头节点
            for(var i = 1 ; i < this.mes.length ; i++){ //遍历身体节点
                if(this.mes[i].x == head.x && this.mes[i].y == head.y){ //判断头结点是否碰撞身体
                    return true;
                }
            }
            return false;
        },
        //判断是否撞墙
        isCrashWell : function(width , height){ //参数为横竖的格子数量
            var head = this.mes[0]; //获取头节点
            if(head.x < 0 || head.y < 0){ //判断是否撞左上墙
                return true;
            }
            if(head.x > (width - 1) || head.y > (height - 1)){ //判断是否撞右下墙
                return true;
            }
            return false;
        },
        //处理吃东西
        handleAdd : function(){
            var head = this.mes[0]; //获取头节点
            if(head.x == point.x && head.y == point.y){ //判断头节点是否碰撞食物节点，食物在外定义
                this.add(); //调用添加身体
                getPoint(); //生成一个节点
                setPoint(); //画一个节点
                score++; //加分
                s.innerHTML = score; //显示分数
            }
        }
    }
    //生成点
    function getPoint(){
        point.x = Math.floor(Math.random(0 , mwid)*60);
        point.y = Math.floor(Math.random(0 , mhei)*60);
    }

    //画点
    function setPoint(){
        c.rect(point.x * unit , point.y * unit , unit , unit);
    }

    //画蛇
    function setShake(){
        for(var i = 0 ; i < shake.mes.length ; i++){
            c.fullStyle = '#ffffff';
            c.lineStyle = '#000000';
            c.rect(shake.mes[i].x * unit , shake.mes[i].y * unit , unit ,unit);
        }
        c.stroke();
    }

    //清屏
    function clear(){
        c.clearRect(0 , 0 , mWidth , mHeight);
    }
    //开始游戏
    function startGame(){
        clearInterval(window.looper); //终止游戏主循环

        //初始化状态
        shake.mes = [{x : 3 , y : 0} , {x : 2 , y : 0} , {x : 1 , y : 0}];
        shake.currOri = 'right';

        c.beginPath();  //开始画笔

        getPoint(); //设置点

        setPoint();

        setShake(); //话蛇

        //画
        c.stroke();

        //游戏主循环
        window.looper = setInterval(function(){
            var method = 'move' + shake.currOri + '()'; //调用方向函数
            eval('shake.' + method); //执行方向方法
            clear(); //清理屏幕
            c.beginPath(); //开始绘制
            shake.handleAdd(); //处理吃东西
            setPoint(point); //设置点
            setShake(); //画蛇
            if(shake.isCrashWell(mwid , mhei)){ //是否撞墙，未使用是否吃自己。想用调用shake.isCrashSelf方法。
                clearInterval(window.looper);
                console.log('you die');
                alert('you die , and your score is ' + score);
            }
        } , 200);
    }
    //键盘监听
    window.onkeyup = function(key){
        var ori = '';
        console.log(key)
        switch(key.keyCode){
            case 37:
                ori = 'left';
                break;
            case 39:
                ori = 'right';
                break;
            case 38:
                ori = 'up';
                break;
            case 40:
                ori = 'down';
                break;
        }
        if(ori == ''){
            return;
        }
        //改变蛇走向
        shake.changeOri(ori);
    }
</script>
</body>
</html>